using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

using InfFront.UICore;

namespace InfFront.InfoSources.Base
{
    public class GetInfoFieldEnumValuesArgs : EventArgs
    {
        public IEnumerable<IInfoFieldEnumItem> Items;
    }

    public delegate void OnGetInfoFieldEnumValues(object sender, GetInfoFieldEnumValuesArgs args);

    /// <summary>
    /// Base IInfoField implementation
    /// </summary>
    [ToolboxItem(false), DesignTimeVisible(false)]
    public abstract partial class InfoField : IInfoField, IComponent, IDisposable, IInfoSourceOwnedObject
    {
        #region Private members
        private string title = string.Empty;
        private string name = string.Empty;
        private bool readOnly = false;
        private bool gridVisible = true;
        private int  order = 99;
        private int  editGroupOrder = 99;
        private InfoFieldGroup editGroup = null;
        private BaseDataType dataType = null;
        private BaseDataType.PersistenceType persistenceType = BaseDataType.PersistenceType.Volatile;
        private bool enabled = true;
        private bool isEnum = false;
        private InfoSource owner;
        private ISite site;
        private EventHandlerList events;
        private InfoSource referencedInfoSource = null;
        private string referencedFieldName = string.Empty;
        private string referencedDisplayFieldName = string.Empty;
        #endregion

        #region Initialization/Construction
        public InfoField()
        {
            Init();
        }

        public InfoField(IContainer container)
        {
            container.Add(this);
            Init();
        }

        private void Init()
        {
        }
        #endregion Initialization/Construction

        #region Public properties

        [Browsable(false)]
        public InfoSource Owner
        {
            get { return owner; }
        }

        void IInfoSourceOwnedObject.SetOwner(InfoSource value)
        {
            owner = value;
        }

        [DefaultValue(true), Description("")]
        public bool Enabled
        {
            get { return enabled; }
            set { enabled = value; }
        }

        [Browsable(false), Description("Unique (within the InfoSource) field name")]
        public string Name
        {
            get 
            { 
                string result = name;
                if (string.IsNullOrEmpty(result))
                    result = Site.Name; 
                if (result == null)
                    result = string.Empty;
                return result;
            }
            set
            {
                name = value;
                if (name == null)
                    name = string.Empty;
            }
        }

        [Category("General"), DefaultValue(""), Description("Is used for labels, column titles and things like that")]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        [Category("General"), DefaultValue(false), Description("Is the field for read only")]
        public bool IsReadOnly
        {
            get { return readOnly; }
            set { readOnly = value; }
        }

        [Category("General"), DefaultValue(true), Description("Is the field allowed to be shown in a grid")]
        public bool IsGridVisible
        {
            get { return gridVisible; }
            set { gridVisible = value; }
        }

        [Category("General"), DefaultValue(false), Description("If the field may have only one of predefined values")]
        public bool IsEnum
        {
            get { return isEnum; }
            set { isEnum = value; }
        }

        [Category("General"), DefaultValue(99), Browsable(false), Description("Field order")]
        public int Order
        {
            get { return order; }
            set { order = value; }
        }

        [Category("Groups"), DefaultValue(99), Description("Field order within the edit group")]
        public int EditGroupOrder
        {
            get { return editGroupOrder; }
            set { editGroupOrder = value; }
        }

        [Category("Groups"), DefaultValue(null), Description("Field edit group")]
        public InfoFieldGroup EditGroup
        {
            get { return editGroup; }
            set { editGroup = value; }
        }

        [Category("General"), DefaultValue(BaseDataType.PersistenceType.Volatile), Description("")]
        public BaseDataType.PersistenceType PersistenceType
        {
            get { return persistenceType; }
            set { persistenceType = value; }
        }

        [Category("General"), DefaultValue(BaseDataType.Type.None), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Description("Field values type")]
        public BaseDataType.Type DataTypeKind
        {
            get
            {
                if (DataTypeParams == null)
                    return BaseDataType.Type.None;
                return DataTypeParams.Kind;
            }
            set
            {
                if (value == DataTypeKind)
                    return;
                switch (value)
                {
                    case BaseDataType.Type.None:
                        DataTypeParams = null;
                        break;
                    case BaseDataType.Type.String:
                        DataTypeParams = new StringDataType(string.Empty);
                        break;
                    case BaseDataType.Type.Integer:
                        DataTypeParams = new IntDataType(string.Empty);
                        break;
                    case BaseDataType.Type.Long:
                        DataTypeParams = new LongDataType(string.Empty);
                        break;
                    case BaseDataType.Type.Boolean:
                        DataTypeParams = new BoolDataType(string.Empty);
                        break;
                    case BaseDataType.Type.Float:
                        DataTypeParams = new FloatDataType(string.Empty);
                        break;
                    case BaseDataType.Type.DateTime:
                        DataTypeParams = new DateTimeDataType(string.Empty);
                        break;
                    case BaseDataType.Type.Enum:
                        DataTypeParams = new EnumDataType(string.Empty);
                        IsEnum = true;
                        break;
                    case BaseDataType.Type.Image:
                        DataTypeParams = new ImageDataType();
                        break;
                    default:
                        throw new NotImplementedException(string.Format("Unsupported DataTypeKind: {0}", value));
                }
            }

        }

        [Category("General"), Description("")]
        public BaseDataType DataTypeParams
        {
            get { return dataType; }
            set { dataType = value; }
        }

        internal IDataTypeTraits DataTypeTraits
        {
            get 
            {
                if (DataTypeParams == null)
                    return DummyDataTraits.Instance;
                return DataTypeParams.Traits;
            }
        }

        [Category("Referencing"), DefaultValue(null), Description("An InfoSource to which the field is referenced")]
        public InfoSource ReferencedInfoSource
        {
            get { return referencedInfoSource; }
            set { referencedInfoSource = value; }
        }

        [Category("Referencing"), DefaultValue(""), Description("Field name within the Referenced InfoSource")]
        public string ReferencedFieldName
        {
            get { return referencedFieldName; }
            set { referencedFieldName = value; }
        }

        [Category("Referencing"), DefaultValue(""), Description("Optional field name (from the the Referenced InfoSource) to display it's value instead of Referenced Field")]
        public string ReferencedDisplayFieldName
        {
            get { return referencedDisplayFieldName; }
            set { referencedDisplayFieldName = value; }
        }

        private static object EVENT_GETENUMVALUES = new object();

        public event OnGetInfoFieldEnumValues OnGetEnumValues
        {
            add { Events.AddHandler(EVENT_GETENUMVALUES, value); }
            remove { Events.RemoveHandler(EVENT_GETENUMVALUES, value); }
        }

        private static object EVENT_DISPOSE = new object();

        public event EventHandler Disposed
        {
            add { Events.AddHandler(EVENT_DISPOSE, value); }
            remove { Events.RemoveHandler(EVENT_DISPOSE, value); }
        }

        #endregion Public properties

        public abstract object GetValue(object obj);
        public abstract void SetValue(object obj, object value);

        #region IInfoField Members

        string IInfoField.GetName()
        {
            return Name;
        }

        string IInfoField.GetTitle()
        {
            return Title;
        }

        bool IInfoField.GetIsReadOnly()
        {
            return IsReadOnly;
        }

        bool IInfoField.GetIsGridVisible()
        {
            return IsGridVisible;
        }

        bool IInfoField.GetIsEnum()
        {
            return IsEnum;
        }

        IEnumerable<IInfoFieldEnumItem> IInfoField.GetEnumValues()
        {
            var handler = (OnGetInfoFieldEnumValues)Events[EVENT_GETENUMVALUES];
            if (handler == null)
            {
                var asEnumParams = DataTypeParams as EnumDataType;
                if (asEnumParams != null && asEnumParams.EnumType != null)
                {
                    var enumItems = new List<IInfoFieldEnumItem>();
                    foreach (var v in Enum.GetValues(asEnumParams.EnumType))
                    {
                        string vString = v.ToString();
                        System.Reflection.FieldInfo fi = asEnumParams.EnumType.GetField(vString);
                        DescriptionAttribute[] attrs =
                            (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                        var item = new InfoFieldEnumItem() { Title = (attrs.Length > 0) ? attrs[0].Description : vString, Value = vString };
                        enumItems.Add(item);
                    }
                    return enumItems;
                }
                return null;
            }
            var args = new GetInfoFieldEnumValuesArgs();
            handler(this, args);
            return args.Items;
        }

        IDataType IInfoField.GetType()
        {
            return DataTypeParams;
        }

        int IInfoField.GetOrder()
        {
            return Order;
        }

        int IInfoField.GetEditGroupOrder()
        {
            return EditGroupOrder;
        }

        IInfoFieldGroup IInfoField.GetEditGroup()
        {
            return EditGroup;
        }

        string IInfoField.GetReferencedInfoSourceName()
        {
            if (ReferencedInfoSource != null)
                return ReferencedInfoSource.Name;
            return null;
        }

        string IInfoField.GetReferencedFieldName()
        {
            return ReferencedFieldName;
        }

        string IInfoField.GetReferencedDisplayFieldName()
        {
            return ReferencedDisplayFieldName;
        }

        #endregion

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            lock (this)
            {
                if ((site != null) && (site.Container != null))
                {
                    site.Container.Remove(this);
                }
                if (events != null)
                {
                    var handler = (EventHandler)Events[EVENT_DISPOSE];
                    if (handler != null)
                        handler(this, EventArgs.Empty);
                    events.Dispose();
                    events = null;
                }
            }
        }

        #endregion

        #region IComponent Members

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
        public ISite Site
        {
            get { return site; }
            set { site = value; }
        }

        #endregion

        #region Private Members

        protected EventHandlerList Events
        {
            get
            {
                if (events == null)
                {
                    events = new EventHandlerList();
                }
                return events;
            }
        }

        #endregion Private Members
    }

    /// <summary>
    /// Base IInfoFieldList implementation
    /// </summary>
    public class InfoFieldList : InfoSourceOwnedListOf<InfoField>
    {
        public InfoFieldList(InfoSource owner)
            : base(owner)
        {
        }

        public class InterfaceContainer : Container<IInfoField>, IInfoFieldList
        {
        }
    }
}
